package com.sunday.common.reactor.e8_Working_With_Time;

import com.sunday.common.reactor.SubscribeUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

/**
 * A.6. 与时间打交道
 * https://projectreactor.io/docs/core/release/reference/#which.time
 */
@Slf4j
public class WorkingWithTime {

    /**
     * 我想将排放与测量的时间联系起来......
     * Timed<T>#elapsed（） 表示自上次以来的持续时间onNext
     * Timed<T>#timestamp（） 表示纪元时间戳的即时表示（毫秒分辨率）
     * 在订阅发生之前：（Flux|单delaySubscription)
     */
    @Test
    public void timed() throws InterruptedException {

        log.info("Timed<T>#elapsed 表示自上次以来的持续时间onNext");
        // Timed<T>#elapsed（） 表示自上次以来的持续时间onNext
        Mono<String> delayedElement = Mono.just("Hello")
                .delayElement(Duration.ofSeconds(3));

        SubscribeUtils.subscribe(delayedElement.elapsed()
                .doOnNext(tuple -> log.info("Elapsed time: " + tuple.getT1() + " ms, value: " + tuple.getT2())));

        TimeUnit.SECONDS.sleep(4);

        log.info("Timed<T>#timestamp 表示纪元时间戳的即时表示（毫秒分辨率）");
        // Timed<T>#timestamp（） 表示纪元时间戳的即时表示（毫秒分辨率）
        SubscribeUtils.subscribe(delayedElement.timestamp()
                .doOnNext(tuple -> log.info("Timestamp: " + tuple.getT1() + " ms, value: " + tuple.getT2())));

        TimeUnit.SECONDS.sleep(4);

        log.info("Timed<T>#delaySubscription 表示自订阅以来的持续时间（而不是上次 onNext）");
        // Timed<T>#delaySubscription 表示自订阅以来的持续时间（而不是上次 onNext）
        SubscribeUtils.subscribe(delayedElement.delaySubscription(Duration.ofSeconds(2))
                .doOnNext(s -> log.info("{}", s)));

        TimeUnit.SECONDS.sleep(6);

    }

    /**
     * 如果发射之间的延迟太大，我希望我的序列被中断：（Flux|Mono#timeout)
     * 如果您希望序列在某个时间段内没有发出任何元素时中断，则可以使用 timeout 操作符。
     */
    @Test
    public void timeout() throws InterruptedException {

        log.info("如果您希望序列在某个时间段内没有发出任何元素时中断，则可以使用 timeout 操作符。");
        // 如果您希望序列在某个时间段内没有发出任何元素时中断，则可以使用 timeout 操作符。
        Mono<String> delayedElement = Mono.just("Hello")
                .delayElement(Duration.ofSeconds(3));

        SubscribeUtils.subscribe(delayedElement.timeout(Duration.ofSeconds(2))
                .doOnNext(s -> log.info("{}", s)));

        TimeUnit.SECONDS.sleep(4);

    }

    /**
     * 我想从时钟中获取时钟的滴答声，定期时间间隔：Flux#interval
     * 您可以使用 interval 操作符来创建一个每隔一段时间就发出一个递增的长整型序列的 Flux。
     */
    @Test
    public void interval() throws InterruptedException {

        log.info("您可以使用 interval 操作符来创建一个每隔一段时间就发出一个递增的长整型序列的 Flux。");
        // 您可以使用 interval 操作符来创建一个每隔一段时间就发出一个递增的长整型序列的 Flux。
        Flux<String> flux =
                Flux.interval(Duration.ofMillis(250))
                        .map(input -> {
                            if (input < 3) return "tick " + input;
                            throw new RuntimeException("boom");
                        })
                        .onErrorReturn("Uh oh");

        SubscribeUtils.subscribe(flux);

        TimeUnit.SECONDS.sleep(4);

    }

    /**
     * 我想在初始延迟后发出一个单曲：static Mono.delay。0
     * 您可以使用 delay 操作符来创建一个在初始延迟后发出一个单曲值的 Mono。
     */
    @Test
    public void delay() throws InterruptedException {

        log.info("您可以使用 delay 操作符来创建一个在初始延迟后发出一个单曲值的 Mono。");
        // 您可以使用 delay 操作符来创建一个在初始延迟后发出一个单曲值的 Mono。
        Mono<String> mono = Mono.delay(Duration.ofSeconds(1))
                .map(i -> "foo");

        SubscribeUtils.subscribe(mono);

        TimeUnit.SECONDS.sleep(2);

    }

    /**
     * 每个 onNext 信号之间：Mono#delayElement、Flux#delayElements
     * 您可以使用 delayElement 操作符来创建一个在每个 onNext 信号之间延迟发出元素的 Mono 或 Flux。
     */
    @Test
    public void delayElement() throws InterruptedException {

        log.info("您可以使用 delayElement 操作符来创建一个在每个 onNext 信号之间延迟发出元素的 Mono 或 Flux。");
        // 您可以使用 delayElement 操作符来创建一个在每个 onNext 信号之间延迟发出元素的 Mono 或 Flux。
        Flux<String> flux = Flux.just("foo", "bar")
                .delayElements(Duration.ofSeconds(2));

        SubscribeUtils.subscribe(flux);

        TimeUnit.SECONDS.sleep(5);

    }



}
